package ua.yandex.collections;

public class MyLinkedList implements MyList {

    private transient Node first;
    private transient Node last;
    private transient int size = 0;

    public MyLinkedList() {
    }

    @Override
    public void add(Object e) {
        add(size, e);
    }

    @Override
    public void add(int index, Object e) {

        if (index == size) {
            Node newNode = new Node(e, null, null);
            if (size == 0) {
                first = newNode;
                last = newNode;
            } else {
                newNode.previous = last;
                last.next = newNode;
                last = newNode;
            }
        } else {
            checkIndexInRange(index);
            Node node = getNode(index);
            Node newNode = new Node(e, node.previous, node);
            node.previous = newNode;
            if (newNode.previous != null) {
                newNode.previous.next = newNode;
            } else {
                first = newNode;
            }


        }
        size++;
    }

    @Override
    public void addAll(Object[] c) {
        for (Object e : c) {
            add(e);
        }
    }

    @Override
    public void addAll(int index, Object[] c) {
        for (int i = 0; i < c.length; i++) {
            add(index + i, c[i]);
        }
    }

    @Override
    public Object get(int index) {
        checkIndexInRange(index);
        return getNode(index).element;
    }

    @Override
    public Object remove(int index) {
        checkIndexInRange(index);
        Node node = getNode(index);
        if (index == size - 1) {
            last = node.previous;
        }
        if (index == 0) {
            first = node.next;
        }
        if (node.previous != null && node.next != null) {
            node.previous.next = node.next;
            node.next.previous = node.previous;
        }
        node.next = null;
        node.previous = null;
        size--;
        return node.element;
    }

    @Override
    public void set(int index, Object e) {
        checkIndexInRange(index);
        getNode(index).element = e;
    }

    @Override
    public int indexOf(Object o) {
        int index = 0;
        Node node = first;
        while (!o.equals(node.element)) {
            index++;
            if (index == size) {
                final int ELEMENT_IS_NOT_IN_LIST = -1;
                return ELEMENT_IS_NOT_IN_LIST;
            }
            node = node.next;

        }
        return index;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        Node node = first;
        for (int i = 0; i < size - 1; i++) {
            node = node.next;
            node.previous.next = null;
            node.previous.previous = null;
        }
        last.previous = null;
        first = null;
        last = null;
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Object[] toArray() {
        Object[] array = new Object[size];
        Node node = first;
        for (int i = 0; i < size; i++) {
            array[i] = node.element;
            node = node.next;
        }
        return array;
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();
        if (!isEmpty()) {
            Node node = first;
            for (int i = 0; i < size; i++) {
                buf.append(node.element.toString());
                if (i < size - 1) {
                    buf.append(", ");
                    node = node.next;
                }
            }
        }
        return buf.toString();
    }

    public void addFirst(Object e) {
        add(0, e);
    } /*- добавляет элемент в начало*/

    public void addLast(Object e) {
        add(e);
    } /*- добавляет элемент в конец*/

    public Object getFirst() {
        checkIndexInRange(0);
        return first.element;
    }

    public Object getLast() {
        checkIndexInRange(size - 1);
        return last.element;
    }

    public Object removeFirst() {
        return remove(0);
    }

    public Object removeLast() {
        return remove(size - 1);
    }

    private static class Node {
        private Object element;
        private Node next;
        private Node previous;

        Node(Object element, Node previous, Node next) {
            this.element = element;
            this.next = next;
            this.previous = previous;
        }


    }

    private void checkIndexInRange(int index) {
        if (index >= size || index < 0) {
            throw new IllegalIndexException();
        }
    }

    private Node getNode(int index) {
        checkIndexInRange(index);
        Node node = first;
        for (int i = 1; i <= index; i++) {
            node = node.next;
        }
        return node;
    }

}



